﻿#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QImageReader>
#include <QMessageBox>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    fatherpath=QCoreApplication::applicationDirPath()+"/";
    // 读取待识别的原始图片
    Loadimg(":/image/00_rawimg.png");
    Showimg(q_img);
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 按照图片的实际格式读取
void MainWindow::Loadimg(QString filepath)
{
    // 尝试加载图像,如果图像加载不成功
    if(!q_img.load(filepath))
    {
        // 按照文件实际格式读取
        QImageReader reader;
        reader.setDecideFormatFromContent(true);
        reader.setFileName(filepath);

        // 仍然无法加载图像
        if (!reader.read(&q_img))
        {
            // 判断文件是否可以读取
            if (reader.canRead())
            {
                // 错误类型:Qt无法识别的图片类型,建议尝试使用OpenCV读取/非图片格式
                QString strError = reader.errorString();
                QMessageBox::information(this,"Open Image Error",strError);
                return;
            }
            else
            {
                // 错误类型:无法读取文件/路径不存在
                QString strError = reader.errorString();
                QMessageBox::information(this,"Open Image Error",strError);
                return;
            }
        }
    }
    m_img=QImage2cvMat(q_img);
}

//  显示自适应大小图片
void MainWindow::Showimg(QImage image)
{
    // 获取图片大小和QLabel控件大小
    int imgwidth=image.width(),imgheight=image.height(),
        labelwidth=ui->label->width(),labelheight=ui->label->height();

    // 计算自适应缩放比
    qreal _width=double(labelwidth)/imgwidth,_height=double(labelheight)/imgheight;
    qreal scale=fmin(_width,_height);

    // 计算缩放后图片的大小
    qreal swidth=imgwidth*scale,sheight=imgheight*scale;

    // 向QLabel控件加载自适应大小图片
    ui->label->setPixmap(QPixmap::fromImage(image.scaled(swidth,sheight)));
}

// 灰度处理
void MainWindow::on_rgb2gray_clicked()
{
    string outpath=(fatherpath+"01_grayimg.png").toStdString();

    // opencv转为灰度图像
    if (m_img.channels()!=1)
        cv::cvtColor(m_img, m_img, COLOR_BGR2GRAY);

    // 将处理后的图像转为QImage格式并自适应显示
    q_img=cvMat2QImage(m_img);
    Showimg(q_img);

    // 保存灰度图像
    imwrite(outpath,m_img);
}

// Mat转QImage
QImage MainWindow::cvMat2QImage(const cv::Mat& mat)
{
    // 8-bits unsigned, NO. OF CHANNELS = 1
    if(mat.type() == CV_8UC1)
    {
        QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
        // Set the color table (used to translate colour indexes to qRgb values)
        image.setColorCount(256);
        for(int i = 0; i < 256; i++)
        {
            image.setColor(i, qRgb(i, i, i));
        }
        // Copy input Mat
        uchar *pSrc = mat.data;
        for(int row = 0; row < mat.rows; row ++)
        {
            uchar *pDest = image.scanLine(row);
            memcpy(pDest, pSrc, mat.cols);
            pSrc += mat.step;
        }
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3
    else if(mat.type() == CV_8UC3)
    {
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
        return image.rgbSwapped();
    }
    else if(mat.type() == CV_8UC4)
    {
        qDebug() << "CV_8UC4";
        // Copy input Mat
        const uchar *pSrc = (const uchar*)mat.data;
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
        return image.copy();
    }
    else
    {
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
}

// QImage转Mat
cv::Mat MainWindow::QImage2cvMat(QImage image)
{
    cv::Mat mat;
    qDebug() << image.format();
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
        break;
    case QImage::Format_RGB888:
        mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
        cv::cvtColor(mat, mat, COLOR_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
        mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
        break;
    }
    return mat;
}

// 灰度图像转二值图像
void MainWindow::on_gray2binary_clicked()
{
    string outpath=(fatherpath+"02_binaryimg.png").toStdString();

    // 直接(全局)阈值化操作cv::threshold()和自适应(局部)阈值化操作cv::adaptiveThreshold(),后者效果较好
    // cv::threshold(m_img,m_img,0,255,cv::THRESH_BINARY|cv::THRESH_OTSU);
    cv::adaptiveThreshold(m_img,m_img,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY,51,0);

    q_img=cvMat2QImage(m_img);
    Showimg(q_img);
    imwrite(outpath,m_img);
}

// 形态学运算
/*
    MORPH_ERODE    = 0, //腐蚀
    MORPH_DILATE   = 1, //膨胀
    MORPH_OPEN     = 2, //开操作
    MORPH_CLOSE    = 3, //闭操作
    MORPH_GRADIENT = 4, //梯度操作
    MORPH_TOPHAT   = 5, //顶帽操作
    MORPH_BLACKHAT = 6, //黑帽操作
    MORPH_HITMISS  = 7
*/
void MainWindow::on_opening_clicked()
{
    string outpath=(fatherpath+"03_openingimg.png").toStdString();

    // 自定义卷积核
    Mat element_open=getStructuringElement(MORPH_RECT,Size(3,3)),
        element_close=getStructuringElement(MORPH_RECT,Size(15,15));
    // 先开后闭
    morphologyEx(m_img,m_img,MORPH_OPEN,element_open);
    morphologyEx(m_img,m_img,MORPH_CLOSE,element_close);

    q_img=cvMat2QImage(m_img);
    Showimg(q_img);
    imwrite(outpath,m_img);
}

// 开始识别,穿线法(由于项目LED字体:1.不清晰 2.包含符号的识别;这里的穿线识别标准是自己设定的,标准穿线法则详见baidu.com)
void MainWindow::on_getoutline_clicked()
{
    string outpath=(fatherpath+"04_getoutline.png").toStdString();

    Mat dst = Mat::zeros(m_img.size(), CV_8UC3),m_imgcopy;
    vector<Mat> splitimg;
    for (int i=0;i<3;i++)
        splitimg.push_back(m_img);
    merge(splitimg,dst);
    //merge(&splitimg[0],splitimg.size(),dst);

    // 获取字符外接轮廓,轮廓值(255, 0, 0)
    vector<vector<Point>> contours_1,contours_2;
    vector<Vec4i> hierarchy;
    findContours(m_img, contours_1, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    morphologyEx(m_img,m_imgcopy,MORPH_CLOSE,getStructuringElement(MORPH_RECT,Size(31,31)));
    findContours(m_imgcopy, contours_2, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    drawContours(dst, contours_1, -1, Scalar(255, 0, 0), 2);

    Rect box0 = boundingRect(Mat(contours_2[0]));

    // 寻找最大外接轮廓
    int xmin=box0.x,ymin=box0.y,xmax=box0.x+box0.width,ymax=box0.y+box0.height,
        area_max=0,area_max_index=0;
    for (int i=0;i<contours_2.size();i++)
    {
        Rect box = boundingRect(Mat(contours_2[i]));

        if (xmin>box.x)
            xmin=box.x;
        if (ymin>box.y)
            ymin=box.y;
        if (xmax<box.x+box.width)
            xmax=box.x+box.width;
        if (ymax<box.y+box.height)
            ymax=box.y+box.height;
        if (area_max<box.area())
        {
            area_max=box.area();
            area_max_index=i;
        }
        rectangle(dst, box, Scalar(0,255,0), 2);
    }
    //qDebug()<<"最大的矩形面积为:\t"<<area_max;
    box0=boundingRect(Mat(contours_2[area_max_index]));

    // 5行7列
    int mcols=7,nrows=5;
    int widthavg=(xmax-xmin)*1.0/mcols,heightavg=(ymax-ymin)*1.0/nrows,
        widthpart=box0.width,heightpart=box0.height;
    vector <int> index,index_error,index_right,indexuser,orcnum;

    // 通过面积大小分离不适合标准穿线法的字符,并对每个字符位置排序
    for (int k=0;k<contours_2.size();k++)
    {
        Rect box = boundingRect(Mat(contours_2[k]));

        if (box.area()<area_max*0.62)
            index_error.push_back(k);
        else
            index_right.push_back(k);

        for (int i=0;i<nrows;i++)
            for (int j=0;j<mcols;j++)
            {
                // 排序
                // 取值范围1.35~1.55
                if (box.x>=xmin+widthavg*j && box.x<xmin+widthavg*j+widthpart*1.4 && box.y>=ymin+heightavg*i && box.y<ymin+heightavg*i+heightpart*1.4)
                {
                    box.x=xmin+widthavg*j;
                    box.y=ymin+heightavg*i;
                    box.width=widthpart*1.4;
                    box.height=heightpart*1.4;
                    index.push_back(k);
                    indexuser.push_back(i*7+j);
                    rectangle(dst, box, Scalar(0,0,255), 1);
                    //box0=boundingRect(Mat(contours_2[k]));
                    //qDebug()<<k<<i*7+j<<":\t"<<box0.area();
                }
            }

    }
    vector <int> indexcopy(index.size());

    // 四段码(符号及1,7)
    for (int i=0;i<index_error.size();i++)
    {
        int a=0,b=0,c=0,d=0,numhex=0,index0=index_error.at(i);
        box0=boundingRect(Mat(contours_2[index0]));
        int width_2=box0.x+box0.width/2,height_2=box0.y+box0.height/2;

        // a段和c段
        for (int j=box0.y;j<box0.y+box0.height;j++)
        {
            Point point(width_2,j);
            if (j<height_2)
                {if (m_img.at<uchar>(point)==255) a=1;}//a
            else
                if (m_img.at<uchar>(point)==255) c=1;//c
        }

        // b段和d段
        for (int k=box0.x;k<box0.x+box0.width;k++)
        {
            Point point(k,height_2);
            if (k<width_2)
                {if (m_img.at<uchar>(point)==255) d=1;}//d
            else
                if (m_img.at<uchar>(point)==255) b=1;//b
        }

        numhex=a*8+b*4+c*2+d;
        for (int l=0;l<index.size();l++)
        {
            if (index0==index.at(l))
                indexcopy.at(l)=numhex;
        }
    }

    // 七段码(符号及1,7)
    for (int i=0;i<index_right.size();i++)
    {
        int a=0,b=0,c=0,d=0,e=0,f=0,g=0,
            numhex=0,index0=index_right.at(i);
        box0=boundingRect(Mat(contours_2[index0]));
        int width_2=box0.x+box0.width/2,height_31=box0.y+box0.height/3,height_32=box0.y+box0.height/3*2;

        // a,g,c段
        for (int j=box0.y;j<box0.y+box0.height;j++)
        {
            Point point(width_2,j);
            if (j<height_31)
                {if (m_img.at<uchar>(point)==255) a=1;}//a
            else if (j>=height_31 && j<=height_32)
                {if (m_img.at<uchar>(point)==255) g=1;}//g
            else
                if (m_img.at<uchar>(point)==255) d=1;//d
        }

        // f,e,b,c段
        for (int k=box0.x;k<box0.x+box0.width;k++)
        {
            Point point1(k,height_31),point2(k,height_32);
            if (k<width_2)
            {
                if (m_img.at<uchar>(point1)==255)//f
                    f=1;
                else if (m_img.at<uchar>(point2)==255)//e
                    e=1;
            }
            else
            {
                if (m_img.at<uchar>(point1)==255)//b
                    b=1;
                else if (m_img.at<uchar>(point2)==255)//c
                    c=1;
            }
        }

        numhex=a*64+b*32+c*16+d*8+e*4+f*2+g;
        for (int l=0;l<index.size();l++)
        {
            if (index0==index.at(l))
                indexcopy.at(l)=numhex;
        }
    }

    //根据numbex值(特征码)识别数码管数字及符号
    for (int i=0;i<indexcopy.size();i++)
    {
        switch (indexcopy.at(i)) {
        case 126:
            indexcopy.at(i)=0;
            break;
        case 15:
            indexcopy.at(i)=1;
            break;
        case 109:
            indexcopy.at(i)=2;
            break;
        case 121:
            indexcopy.at(i)=3;
            break;
        case 51:
            indexcopy.at(i)=4;
            break;
        case 91:
            indexcopy.at(i)=5;
            break;
        case 31:
            indexcopy.at(i)=6;
            break;
        case 12:
            indexcopy.at(i)=7;
            break;
        case 112:
            indexcopy.at(i)=7;
            break;
        case 127:
            indexcopy.at(i)=8;
            break;
        case 115:
            indexcopy.at(i)=9;
            break;
        case 7:
            indexcopy.at(i)=-1;
            break;
        case 13:
            indexcopy.at(i)=-2;
            break;
        case 14:
            indexcopy.at(i)=-3;
            break;
        case 11:
            indexcopy.at(i)=-4;
            break;
        }
        int index0=index.at(i);
        box0=boundingRect(Mat(contours_2[index0]));
        Point point(box0.x,box0.y+box0.height);

        putText(dst,to_string(indexcopy.at(i)),point,FONT_HERSHEY_SIMPLEX,1,Scalar(0,0,255),3);
    }

    for (int i=0;i<index.size();i++)
    {
        for (int j=0;j<index.size();j++)
        {
            if (i==indexuser.at(j))
                orcnum.push_back(j);
        }
    }

    for (int i=0;i<index.size();i++)
    {
        for (int j=0;j<index.size();j++)
        {
            if (orcnum.at(i)==j)
            {
                orcnum.at(i)=indexcopy.at(j);
                break;
            }
        }
    }

    for (int i=0;i<index.size();i++)
    {
        qDebug()<<i<<orcnum.at(i);
    }
    q_img=cvMat2QImage(dst);
    Showimg(q_img);
    imwrite(outpath,dst);
}

// 备用测试方案(标准LED字码穿线法,来自csdn)
void MainWindow::on_maincode_clicked()
{
    Mat threshold_output=m_img.clone();
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours( threshold_output, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);


    vector<vector<Point> > contours_poly( contours.size() );
    vector<Rect> boundRect( contours.size() );

    for( unsigned int i = 0; i < contours.size(); i++ )
    {
        approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
        boundRect[i] = boundingRect( Mat(contours_poly[i]) );
    }
    Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
    for( int unsigned i = 0; i<contours.size( ); i++ )
    {
        Scalar color = Scalar( 125,125,90 );
        drawContours( drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        rectangle( threshold_output, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
    }

    imshow("效果图",threshold_output);


    Rect roirect;
    for (int unsigned i = 0;i<boundRect.size();i++)
    {
        roirect = boundRect[i];
        Mat final;
        final = m_img(boundRect[i]);
        if ((double(boundRect[i].height)/double(boundRect[i].width))>3.5&& (double(boundRect[i].height)/double(boundRect[i].width))<5)
        {
            roirect = boundRect[i];
            Mat final;
            final = m_img(roirect);
            int num;
            num = 1;

        }
        else if ((double(boundRect[i].height)/double(boundRect[i].width))>1.35&& (double(boundRect[i].height)/double(boundRect[i].width))<2.4)
        {
            roirect = boundRect[i];
            Mat final;
            final = m_img(roirect);
            int num;
            num = getnum(final);

            if (num != -1)
            {
                cout <<"get the num: "<<num<<endl;
            }
        }
    }

}

// 仅供备用方案调用
int MainWindow::getnum(Mat image)
{
    int h = image.rows;
    int w = image.cols;
    int result;
    bool a1,a2,a3,a4,a5,a6,a7;

    if(cross(image,int(0.5*w),0,int(0.5*w),int(0.25*h)))a1 = true;
    else a1=false;
    if(cross(image,0,int(0.25*h),int(0.5*w),int(0.25*h)))a2 = true;
    else a2=false;
    if(cross(image,int(0.5*w),int(0.25*h),int(w),int(0.25*h)))a3 = true;
    else a3=false;

    if(cross(image,int(0.5*w),int(0.25*h),int(0.5*w),int(0.75*h)))a4 = true;
    else a4=false;
    if(cross(image,0,int(0.75*h),int(0.5*w),int(0.75*h)))a5 = true;
    else a5=false;
    if(cross(image,int(0.5*w),int(0.75*h),int(w),int(0.75*h)))a6 = true;
    else a6=false;
    if(cross(image,int(0.5*w),int(0.75*h),int(0.5*w),int(h)))a7 = true;
    else a7=false;

    if(a1 !=0&&a2 ==0&&a3 !=0&&a4 !=0&&a5 !=0&&a6 ==0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 2;
    else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 3;
    else if(a1 ==0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 ==0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 4;
    else if(a1 !=0&&a2 !=0&&a3 ==0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 5;
    else if(a1 !=0&&a2 !=0&&a3 ==0&&a4 !=0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 6;
    else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 ==0&&a5 ==0&&a6 !=0&&a7 ==0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 7;
    else if(a1 !=0&&a2 ==0&&a3 !=0&&a4 ==0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 7;
    else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 8;
    else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 !=0&&a5 ==0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 9;
    else if(a1 !=0&&a2 !=0&&a3 !=0&&a4 ==0&&a5 !=0&&a6 !=0&&a7 !=0&&(getgrey(image,w*0.5,h*0.25) <= 150)&&(getgrey(image,w*0.5,h*0.75) <= 150)) result = 0;
    else
    {
        result = -1;
    }
    return result;
}

// 仅供备用方案调用
bool MainWindow::cross(Mat image,int x1,int y1,int x2,int y2)
{
    if (x1 == x2)
    {
        for (int y = y1;y<y2;y++)
        {
            if(getgrey(image,x1,y)>200)return true;
        }
        return false;
    }
    else
    {
        for (int x = x1;x<x2;x++)
        {
            if(getgrey(image,x,y1)>200)return true;
        }
        return false;
    }
}

// 仅供备用方案调用
int MainWindow::getgrey(Mat image,int x,int y)
{
    int w = image.cols;
    Mat_<uchar>::iterator it = image.begin<uchar>();
    Mat_<uchar>::iterator itend = image.end<uchar>();
    int pixel = *(it + y * w + x);
    return pixel;
}
